import textwrap

from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from rest_framework.mixins import ListModelMixin
from rest_framework.decorators import action
from rest_framework.response import Response

from orgs.models import Organization, ROLE as ORG_ROLE
from users.models.user import User
from common.const.http import POST
from common.drf.api import JMSModelViewSet, JmsGenericViewSet
from common.permissions import IsValidUser, IsObjectOwner
from common.utils.timezone import dt_parser
from common.drf.serializers import EmptySerializer
from perms.models.asset_permission import AssetPermission, Asset
from perms.models import Action
from assets.models.user import SystemUser
from ..exceptions import (
    ConfirmedAssetsChanged, ConfirmedSystemUserChanged,
    TicketClosed, TicketActionAlready, NotHaveConfirmedAssets,
    NotHaveConfirmedSystemUser
)
from .. import serializers
from ..models import Ticket
from ..permissions import IsAssignee


class RequestAssetPermTicketViewSet(JMSModelViewSet):
    queryset = Ticket.origin_objects.filter(type=Ticket.TYPE.REQUEST_ASSET_PERM)
    serializer_classes = {
        'default': serializers.RequestAssetPermTicketSerializer,
        'approve': EmptySerializer,
        'reject': EmptySerializer,
        'close': EmptySerializer,
        'assignees': serializers.AssigneeSerializer,
    }
    permission_classes = (IsValidUser,)
    filter_fields = ['status', 'title', 'action', 'user_display', 'org_id']
    search_fields = ['user_display', 'title']

    def _check_can_set_action(self, instance, action):
        if instance.status == instance.STATUS.CLOSED:
            raise TicketClosed
        if instance.action == action:
            action_display = instance.ACTION.get(action)
            raise TicketActionAlready(detail=_('Ticket has %s') % action_display)

    def _get_extra_comment(self, instance):
        meta = instance.meta
        ips = ', '.join(meta.get('ips', []))
        confirmed_assets_id = meta.get('confirmed_assets', [])
        confirmed_system_users_id = meta.get('confirmed_system_users', [])
        confirmed_assets = Asset.objects.filter(id__in=confirmed_assets_id)
        confirmed_system_users = SystemUser.objects.filter(id__in=confirmed_system_users_id)
        confirmed_assets_display = ', '.join([str(i) for i in confirmed_assets])
        confirmed_system_users_display = ', '.join([str(i) for i in confirmed_system_users])

        return textwrap.dedent('''
            {}: {}
            {}: {}
            {}: {}
            {}: {}
            {}: {}
        '''.format(
            _('IP group'), ips,
            _('Hostname'), meta.get('hostname', ''),
            _('System user'), meta.get('system_user', ''),
            _('Confirmed assets'), confirmed_assets_display,
            _('Confirmed system users'), confirmed_system_users_display
        ))

    @action(detail=True, methods=[POST], permission_classes=[IsAssignee, IsValidUser])
    def reject(self, request, *args, **kwargs):
        instance = self.get_object()
        action = instance.ACTION.REJECT
        self._check_can_set_action(instance, action)
        instance.perform_action(action, request.user, self._get_extra_comment(instance))
        return Response()

    @action(detail=True, methods=[POST], permission_classes=[IsAssignee, IsValidUser])
    def approve(self, request, *args, **kwargs):
        instance = self.get_object()
        action = instance.ACTION.APPROVE
        self._check_can_set_action(instance, action)

        meta = instance.meta
        confirmed_assets = meta.get('confirmed_assets', [])
        assets = list(Asset.objects.filter(id__in=confirmed_assets))
        if not assets:
            raise NotHaveConfirmedAssets(detail=_('Confirm assets first'))

        if len(assets) != len(confirmed_assets):
            raise ConfirmedAssetsChanged(detail=_('Confirmed assets changed'))

        confirmed_system_users = meta.get('confirmed_system_users', [])
        if not confirmed_system_users:
            raise NotHaveConfirmedSystemUser(detail=_('Confirm system-users first'))

        system_users = SystemUser.objects.filter(id__in=confirmed_system_users)
        if system_users is None:
            raise ConfirmedSystemUserChanged(detail=_('Confirmed system-users changed'))

        instance.perform_action(instance.ACTION.APPROVE,
                                request.user,
                                self._get_extra_comment(instance))
        self._create_asset_permission(instance, assets, system_users)
        return Response({'detail': _('Succeed')})

    @action(detail=True, methods=[POST], permission_classes=[IsAssignee | IsObjectOwner])
    def close(self, request, *args, **kwargs):
        instance = self.get_object()
        instance.status = Ticket.STATUS.CLOSED
        instance.save()
        return Response({'detail': _('Succeed')})

    def _create_asset_permission(self, instance: Ticket, assets, system_users):
        meta = instance.meta
        actions = meta.get('actions', Action.CONNECT)

        ap_kwargs = {
            'name': _('From request ticket: {} {}').format(instance.user_display, instance.id),
            'created_by': self.request.user.username,
            'comment': _('{} request assets, approved by {}').format(instance.user_display,
                                                                     instance.assignee_display),
            'actions': actions,
        }
        date_start = dt_parser(meta.get('date_start'))
        date_expired = dt_parser(meta.get('date_expired'))
        if date_start:
            ap_kwargs['date_start'] = date_start
        if date_expired:
            ap_kwargs['date_expired'] = date_expired

        ap = AssetPermission.objects.create(**ap_kwargs)
        ap.system_users.add(*system_users)
        ap.assets.add(*assets)
        ap.users.add(instance.user)

        return ap


class AssigneeViewSet(ListModelMixin, JmsGenericViewSet):
    serializer_class = serializers.AssigneeSerializer
    permission_classes = (IsValidUser,)
    filter_fields = ('username', 'email', 'name', 'id', 'source')
    search_fields = filter_fields

    def get_queryset(self):
        user = self.request.user
        org_id = self.request.query_params.get('org_id', Organization.DEFAULT_ID)

        q = Q(role=User.ROLE.ADMIN)
        if org_id != Organization.DEFAULT_ID:
            q |= Q(m2m_org_members__role=ORG_ROLE.ADMIN, orgs__id=org_id, orgs__members=user)
        org_admins = User.objects.filter(q).distinct()
        return org_admins
